package fr.enseirb.mycub.service;

import java.util.ArrayList;
import java.util.List;

import fr.enseirb.mycub.R;
import fr.enseirb.mycub.json.JSONHandler;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;

//http://rainulf.ca/Android-Development-Part-2-Service-212.html
//http://stackoverflow.com/questions/6648153/listening-to-bound-location-service-from-an-activity

public class LocationService extends Service implements LocationListener {

	private List<ILocationServiceListener> listeners = null;
	
	// Constantes 
	public static final int DISTANCE_UNAVAILABLE = -1;
	public static final int MINIMUM_DISTANCE_NETWORK = 50;
	public static final int MINIMUM_DISTANCE_GPS = 10;
	// actualisation toute les X ms
	public static final int MINIMUM_ELAPSED_NETWORK = 10000;
	public static final int MINIMUM_ELAPSED_GPS = 5000;
	
	
	
	private boolean mIsGpsUsed = false;
	private boolean mIsNetworkAvailable = true;
	private boolean mIsGpsAvailable = true;
	private boolean mAskForGps = true;
	private boolean mAskForLocation = true;
	private boolean mIsInPause = true;
	private LocationManager mLocationManager = null;
	private Location mLastFix = null;
	private final IBinder mBinder = new LocationBinder();
	private Runnable mStopServiceRunnable = null;
	private Handler mDelayedHandler = null;

	/**
	 * Class used for the client Binder. Because we know this service always
	 * runs in the same process as its clients, we don't need to deal with IPC.
	 */
	public class LocationBinder extends Binder {
		public LocationService getService() {
			// Return this instance of LocalService so clients can call public
			// methods
			return LocationService.this;
		}
	}

	/*
	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
	}
	*/
	
	@Override
	public void onCreate() {
		mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		listeners = new ArrayList<ILocationServiceListener>();
		mStopServiceRunnable = new Runnable() {

			@Override
			public void run() {
				stopSelf();
			}
		};
		mDelayedHandler = new Handler();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

	// Ajout d'un listener
	public void addListener(ILocationServiceListener listener) {
		if (mIsInPause) {
			new Thread(new Runnable() {

				@Override
				public void run() {
					enableMyLocation();
				}
			}).run();
		}
		listeners.add(listener);
		listener.onLocationChanged(mLastFix, true);
		mDelayedHandler.removeCallbacks(mStopServiceRunnable);
		if (!mIsGpsAvailable && !mIsNetworkAvailable && mAskForLocation) {
			listener.onLocationProvidersChanged(R.id.no_location_provider);
			mAskForLocation = false;
		} else if (!mIsGpsAvailable && mIsNetworkAvailable && mAskForGps) {
			listener.onLocationProvidersChanged(R.id.enable_gps);
			mAskForGps = false;
		}
	}

	// Suppression d'un listener
	public void removeListener(ILocationServiceListener listener) {
		listeners.remove(listener);
		if (listeners.isEmpty()) {
			mDelayedHandler.postDelayed(mStopServiceRunnable, 2000);
		}
	}

	// Notification des listeners
	private void fireLocationChanged(Location l) { //transmets � toutes les activit�s utilisant le GPS ma nouvelle position
		JSONHandler.longitude = l.getLongitude();
		JSONHandler.latitude = l.getLatitude();
		for (ILocationServiceListener listener : listeners) {
			listener.onLocationChanged(l, false);
		}
	}

	private void fireShowNoLocationProvider() {
		int size = listeners.size();
		if (size != 0) {
			ILocationServiceListener listener = listeners.get(size - 1);
			listener.onLocationProvidersChanged(R.id.no_location_provider);
			mAskForLocation = false;
		}
	}

	private void fireShowAskForGps() {
		int size = listeners.size();
		if (size != 0) {
			ILocationServiceListener listener = listeners.get(size - 1);
			listener.onLocationProvidersChanged(R.id.enable_gps);
			mAskForGps = false;
		}
	}

	@Override
	public void onDestroy() {
		disableMyLocation();
		this.listeners.clear();
	}

	public boolean isGpsEnabled() {
		return mIsGpsAvailable;
	}

	public boolean isProviderEnabled() {
		return mIsGpsAvailable || mIsNetworkAvailable;
	}

	public boolean isLocationAvailable() {
		return mLastFix != null;
	}

	public synchronized void enableMyLocation() {
		if (!mIsInPause)
			return;
		mIsInPause = false;
		List<String> providers = mLocationManager.getProviders(false);
		if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
			mLocationManager.requestLocationUpdates(
					LocationManager.NETWORK_PROVIDER, MINIMUM_ELAPSED_NETWORK,
					MINIMUM_DISTANCE_NETWORK, this);
		}
		if (providers.contains(LocationManager.GPS_PROVIDER)) {
			mLocationManager.requestLocationUpdates(
					LocationManager.GPS_PROVIDER, MINIMUM_ELAPSED_GPS,
					MINIMUM_DISTANCE_GPS, this);
		}
	}

	public synchronized void disableMyLocation() {
		if (mIsInPause)
			return;
		mIsInPause = true;
		mLocationManager.removeUpdates(this);
		mIsGpsUsed = false;
		mIsNetworkAvailable = true;
		mIsGpsAvailable = true;
	}

	public Location getMyLocation() {
		return mLastFix;
	}

	@Override
	public synchronized void onLocationChanged(Location location) {
		
		if (mLastFix != null && // On a r�ussi le positionnement et
				(mLastFix.distanceTo(location) //la distance de la derni�re position connu
						< // est inf�rieur
				(mIsGpsUsed ? MINIMUM_DISTANCE_GPS : MINIMUM_DISTANCE_NETWORK)) // � l'�cart acceptable selon la pr�cision
			)
			return;
		// On a r�ellement boug�...
		if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) { // fix gps
			mLastFix = location;
			fireLocationChanged(location);
			mIsGpsUsed = true;
		} else if (location.getProvider().equals(LocationManager.NETWORK_PROVIDER) && !mIsGpsUsed) { //fix reseau
			if (mLastFix == null) {// On ne remplace pas un fix du gps par un fix reseau
				mLastFix = location;
				fireLocationChanged(location);
			}
		}
	}

	@Override
	public void onProviderDisabled(String provider) {
		if (provider.equals(LocationManager.GPS_PROVIDER)) {
			mIsGpsAvailable = false;
			mIsGpsUsed = false;
		} else if (provider.equals(LocationManager.NETWORK_PROVIDER)) {
			mIsNetworkAvailable = false;
		}
		if (!mIsNetworkAvailable && !mIsGpsAvailable) {
			mLastFix = null;
			fireLocationChanged(null);
		}
		if (!mIsGpsAvailable && !mIsNetworkAvailable && mAskForLocation) {
			fireShowNoLocationProvider();
		} else if (!mIsGpsAvailable && mIsNetworkAvailable && mAskForGps) {
			fireShowAskForGps();
		}
	}

	@Override
	public void onProviderEnabled(String provider) {
		if (provider.equals(LocationManager.GPS_PROVIDER)) {
			mIsGpsAvailable = true;
			mAskForGps = true;
			mAskForLocation = true;
		}
		if (provider.equals(LocationManager.NETWORK_PROVIDER)) {
			mIsNetworkAvailable = true;
			mAskForLocation = true;
		}
	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
	}
}